iT邦幫忙

2021 iThome 鐵人賽

DAY 26
1

Keyword: Ktor MockEngine, Unit Test
直到27日,完成KMM的測試功能放在
KMMDay27


有了基礎的Mock環境,接下來我們要來Mock 網路請求.Ktor官方也有提供假的Mock引擎以便我們使用,不用自己再手動開發一個.

同樣的,在gradle(shared)內我們進行Mock Engine的引用,我們是測試所以這次是放在commonTest內

//這是build.gradle.kts的soureSet內的Kotlin
val commonTest by getting {
            dependencies {
                ...
                implementation(Develop.Ktor.commonTest)
                implementation(Develop.Ktor.cio)
								...
            }
        }

而路徑如下

//這是在buildSrc內的Kotlin
object Ktor{
        val commonTest = "io.ktor:ktor-client-mock:${Versions.ktor}"
        val cio = "io.ktor:ktor-client-cio:${Versions.ktor}"
    }

有了MockEnginge後,我們來建立一個使用MockEngine的MockApi

在commonTest下建立一個Mock package,然後裡面放入我們的CafeApi的Mock

//這是在commonTest下的Kotlin
class CafeApiMock(mockEngine:HttpClientEngine) : CafeApi {
    companion object {
        val tag = CafeApiMock::class.simpleName
    }

    private val client = HttpClient(mockEngine) {
        install(JsonFeature) {
            serializer = KotlinxSerializer()
        }
    }

    override suspend fun fetchCafeFromApi(city: String): List<CafeResponseItem> {
        return client.get("https://cafenomad.tw/api/v1.2/cafes/")
    }
}

可以看到大部分都跟我們正常的CafeApiImpl相同,只是可以提供一個mockEngine來建立這個物件.

然後我們在commonTest下建立這個物件的測試流程,注意記得讓commonTest去繼承我們昨天寫好的BaseTest,這樣就能利用其中的方法建立一個給測試使用的coroutine,並且對雙平台的獨立測試來說沒有差別,KMM會幫我們自動選擇合適的實作.

//這是在commonTest底下的Kotlin語言
class CommonGreetingTest : BaseTest(){//繼承了BaseTest來使用Coroutine
    @Test//標註Test說明這是一個Test Case
    fun KtorTest() {
        
    }
}

然後我們可以在KtorTest內建立我們的mockEngine,來控制我們的狀態與內容

val mockEngine = MockEngine { request ->//不管什麼request
            respond(//都會回傳這樣
                content = ByteReadChannel("""[{"id":"000703fe-cf8a-43c8-bd83-c90cfd61915f","name":"覺旅咖啡","address":"太陽系","city":"taipei"}]"""),
                status = HttpStatusCode.OK,
                headers = headersOf(HttpHeaders.ContentType, "application/json")
            )
        }

並且把這個mockEngine丟進去產生CafeApiMock

val apiMock = CafeApiMock(mockEngine)

最後建立回傳物件以作比較

val fakeResponseItem = CafeResponseItem("000703fe-cf8a-43c8-bd83-c90cfd61915f","太陽系","覺旅咖啡","taipei")
val responseItem = listOf(fakeResponseItem)

然後在Coroutine環境下進行測試

runTest{
    assertEquals(apiMock.fetchCafeFromApi("taipei"),result)
}

整份的測試內容如下

@Test
    fun KtorTest() {
        val mockEngine = MockEngine { request ->
            respond(
                content = ByteReadChannel("""[{"id":"000703fe-cf8a-43c8-bd83-c90cfd61915f","name":"覺旅咖啡","address":"太陽系","city":"taipei"}]"""),
                status = HttpStatusCode.OK,
                headers = headersOf(HttpHeaders.ContentType, "application/json")
            )
        }
        val apiMock = CafeApiMock(mockEngine)
        val fakeResponseItem = CafeResponseItem("000703fe-cf8a-43c8-bd83-c90cfd61915f","太陽系","覺旅咖啡","taipei")
        val result = listOf(fakeResponseItem)
        runTest{
            assertEquals(apiMock.fetchCafeFromApi("taipei"),result)
        }
    }

可以點旁邊的綠色三角形來跑跑看.

明天我們會來跑看看SQLDelight的測試內容


上一篇
Day 25: 準備假的Coroutine,讓外面世界不會影響我!
下一篇
Day 27:DB也是假的 建立Mock SQLDelight
系列文
挑戰 Kotlin Multiplatform Mobile 跨平台開發,透過共同的Kotlin模組同時打造iOS與Android應用!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言